
export default function createConfig({ Manipulator, isElement, toType }) {
  /**
   * Class definition
   */

  class Config {
    // Getters
    static get Default() {
      return {}
    }

    static get DefaultType() {
      return {}
    }

    static get NAME() {
      throw new Error(
        'You have to implement the static method "NAME", for each component!',
      )
    }

    _getConfig(config) {
      config = this._mergeConfigObj(config)
      config = this._configAfterMerge(config)
      this._typeCheckConfig(config)
      return config
    }

    _configAfterMerge(config) {
      return config
    }

    _mergeConfigObj(config, element) {
      const jsonConfig = isElement(element)
        ? Manipulator.getDataAttribute(element, 'config')
        : {} // try to parse

      return {
        ...this.constructor.Default,
        ...(typeof jsonConfig === 'object' ? jsonConfig : {}),
        ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),
        ...(typeof config === 'object' ? config : {}),
      }
    }

    _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
      for (const [property, expectedTypes] of Object.entries(configTypes)) {
        const value = config[property]
        const valueType = isElement(value) ? 'element' : toType(value)

        if (!new RegExp(expectedTypes).test(valueType)) {
          throw new TypeError(
            `${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`,
          )
        }
      }
    }
  }

  return Config
}
